import processing.serial.*;
import promidi.*;

MidiIO midiIO;
MidiOut midiOut;

//Midi
String preferredMidiOutput = "MIDI Yoke NT:  5";
int controllerValue = 0;

//Constants
int NUM_INPUTS = 6;               //number of analog inputs
int SERIAL_UPDATE_RATE = 10;     //period between updates in milliseconds
int MAX_X = 512;                  //window size x
int MAX_Y = 256;                  //window size y
int EDGE_THRESH = 11;
boolean PROCESS = false;

//Arduino
int ANALOGVAL_MAX = 1024;        //maximum analog value
int[] analogValue = new int[NUM_INPUTS];
int[] analogValueOld = new int[NUM_INPUTS];
boolean[] switchState = new boolean[NUM_INPUTS];
boolean[] switchStateOld = new boolean[NUM_INPUTS];

//rotary
int direction = 0;
int directionHold = 0;
int[] switchBuffer = {3,3,3};
int old_in0 = 0;
int old_in1 = 0;
int old_in2 = 0;
int last_edge = 0;
int last_edge1 = 0;
int last_edge2 = 0;

int switchTime = 1;
int lastVel = 1;
//Serial
Serial port;

// buffer and value variable for each value and boolean to switch between values
boolean firstContact = false;
int serialCount = 0;
int[] serialInArray = new int[NUM_INPUTS*2];
int[] serialValue = new int[NUM_INPUTS];

//Drawing

int NUM_COLUMNS = 6;
int NUM_ROWS = 4;
int columnWidth = MAX_X / NUM_COLUMNS;
int rowHeight = MAX_Y / NUM_ROWS;

color off = color(200,200,255);
color on = color(84, 145, 158);
PFont fontA;

//General
int i, j, k;

void setup() {

  size(MAX_X, MAX_Y);
  // Set the font and its size (in units of pixels)
  fontA = loadFont("LucidaSans-16.vlw");
  textFont(fontA, 16);
  
  initializeSerial(1, 9600);
  initializeVars();
  initializeMidi();
}

void draw() {

  //PROCESS THE LATEST SERIAL INPUT
  //if (PROCESS) 
  //processInput();

  //PROCESS AND OUTPUT MIDI
  processMidi();
  
  //UPDATE THE DISPLAY
  updateDisplay();
  
  //IF NO SERIAL CONTACT HAS BEEN MADE
  if (firstContact == false) {
    delay(300);
    port.write(65);
  } 
  
  //delay(10);
}

void initializeVars()
{
  for (int i = 0; i < NUM_INPUTS; i++)
  {
    analogValue[i] = 0;
    analogValueOld[i] = -1;
    switchState[i] = false;
    switchStateOld[i] = false;
  }
}

//To Get the on/off value from the analog input which is sent to the text file the webpage reads from on the server
void processInput()
{
  for (int i = 3; i < NUM_INPUTS; i++) {
    if (analogValue[i] > 500)
      switchState[i] = true;
    else
      switchState[i] = false;
  }
  direction = 0;
  
  if (switchState[3]&&(!switchStateOld[3])) {
    if(last_edge==5&&last_edge1 == 4 && last_edge2 == 3)
      direction=1;
     else if(last_edge == 1 && last_edge1 == 2 && last_edge2 == 3)
      direction=-1;
    switchStateOld[3] = switchState[3];
    last_edge2 = last_edge1;
    last_edge1 = last_edge;
    last_edge = 0;
   } else if((!switchState[5])&&switchStateOld[5]) {
     if(last_edge == 0 && last_edge1 == 5 && last_edge2 == 4)
	direction=1;
     else if(last_edge == 2 && last_edge1 == 3 && last_edge2 == 4)
       direction=-1;
     switchStateOld[5] = switchState[5];
     last_edge2 = last_edge1;
     last_edge1 = last_edge;
     last_edge = 1;
    }else if(switchState[4] && (!switchStateOld[4])){
      if(last_edge == 1 && last_edge1 == 0 && last_edge2 == 5)
        direction=1;
      else if(last_edge == 3 && last_edge1 == 4 && last_edge2 == 5)
	direction=-1;
      switchStateOld[4] = switchState[4];
      last_edge2 = last_edge1;
      last_edge1 = last_edge;
      last_edge = 2;
    }else if(!switchState[3] && switchStateOld[3]){
      if(last_edge == 2 && last_edge1 == 1 && last_edge2 == 0)
        direction = directionVel(1);
      else if(last_edge == 4 && last_edge1 == 5 && last_edge2 == 0)
        direction = directionVel(-1);
      switchStateOld[3] = switchState[3];
      last_edge2 = last_edge1;
      last_edge1 = last_edge;
      last_edge = 3;
    }else if(switchState[5] && (!switchStateOld[5])){
      if(last_edge == 3 && last_edge1 == 2 && last_edge2 == 1)
        direction = directionVel(1);
      else if(last_edge == 5 && last_edge1 == 0 && last_edge2 == 1)
        direction = directionVel(-1);
      switchStateOld[5] = switchState[5];
      last_edge2 = last_edge1;  
      last_edge1 = last_edge;
      last_edge = 2;
    }else if((!switchState[4]) && switchStateOld[4]){
      if(last_edge == 4 && last_edge1 == 3 && last_edge2 == 2)
        direction = directionVel(1);
      else if(last_edge == 0 && last_edge1 == 1 && last_edge1 == 2)
        direction = directionVel(-1);
      switchStateOld[4] = switchState[4];
      last_edge2 = last_edge1;
      last_edge1 = last_edge;
      last_edge = 3;
    }
  
  
  
  if (direction != 0)
  {
    directionHold = direction;
  }
}

int directionVel(int dir)
{
  int t = millis();
  int vel = dir*constrain((200-t-switchTime)/20,1,10); 
  lastVel = t-switchTime;
  switchTime = t;
  return dir;
}

void processMidi()
{
  if (direction != 0)
  {
    controllerValue = constrain(controllerValue+direction, 0, 127);
      midiOut.sendController(
        new Controller(20,controllerValue)
      );
  }
}

//Graphical interface update, to show if processing is picking up the analog input in a nice pretty fashion.
void updateDisplay()
{
  background(off);
  stroke(on);
  text("time = " + (lastVel), 2, rowHeight + (rowHeight/2));
  text("cont = " + controllerValue, 2, rowHeight * 2 + (rowHeight/2));
  text("dir = " + directionHold, 2, rowHeight * 3 + (rowHeight/2));
  
  for (int i = 3; i < NUM_INPUTS; i++) 
  {
    stroke(255);
    fill(255);
   //Draw circles representing analog value
    ellipse(i*columnWidth+columnWidth/2, rowHeight/2,
      analogValue[i] / (ANALOGVAL_MAX / rowHeight),analogValue[i] / (ANALOGVAL_MAX / rowHeight));
  
   //Draw dots representing ON/OFF state
    if (switchState[i])
      ellipse(i*columnWidth+columnWidth/2, rowHeight+rowHeight/2, rowHeight/4, rowHeight/4);
 
   //Print values data
    stroke(0,0,0);
    fill(0,0,0);
    text(analogValue[i], i * columnWidth + columnWidth/3, rowHeight * 2 + (rowHeight/2));
    if (switchState[i]) 
      text("ON", i * columnWidth + columnWidth/3, rowHeight * 3 + (rowHeight/2));
  }
}



void initializeMidi()
{
   //get an instance of MidiIO
  midiIO = MidiIO.getInstance(this);
  
  //print a list with all available devices
  midiIO.printDevices();
  
  midiOut = midiIO.getMidiOut(1, preferredMidiOutput);
}

void initializeSerial(int portNum, int baudrate)
{
  // establish serial port connection      
  // port = new Serial(this, portname, baudrate);
  println("Available serial ports:");
  println(Serial.list());

  // Uses the first available port
  port = new Serial(this, Serial.list()[portNum], baudrate);
  println(port);                                                                   
  port.write(65);
}

void serialEvent(Serial port) {
  // if this is the first byte received, 
  // take note of that fact:
  if (firstContact == false) {
    firstContact = true; 
  }
  // Add the latest byte from the serial port to array:
  serialInArray[serialCount] = port.read();
  serialCount++;

  // If we have 12 bytes:
  if (serialCount > 11 ) {
    for (i=0;i<6;i++) {
      serialValue[i] = serialInArray[i*2];
      serialValue[i] <<= 8;
      serialValue[i] = serialValue[i] + serialInArray[i*2 + 1];
      analogValue[i] = serialValue[i];
     
    }
    // Reset serialCount:
    PROCESS = true;
    processInput();
    serialCount = 0;
    port.write(65);
  } else PROCESS = false;
}


